using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using OpenIddict.Server.AspNetCore;
using OpenIddict.Validation.AspNetCore;
using System;
using System.Collections.Generic;
using System.IO;
using Volo.Abp;
using Volo.Abp.Account.Web;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.AntiForgery;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars;
using Volo.Abp.AspNetCore.Mvc.UI.Theming;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.Auditing;
using Volo.Abp.Autofac;
using Volo.Abp.AutoMapper;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.FeatureManagement;
using Volo.Abp.Identity.Web;
using Volo.Abp.Localization;
using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Modularity;
using Volo.Abp.OpenIddict;
using Volo.Abp.PermissionManagement;
using Volo.Abp.Security.Claims;
using Volo.Abp.Studio.Client.AspNetCore;
using Volo.Abp.Swashbuckle;
using Volo.Abp.Timing;
using Volo.Abp.UI.Navigation;
using Volo.Abp.UI.Navigation.Urls;
using Volo.Abp.VirtualFileSystem;
using YaAppoint.EntityFrameworkCore;
using YaAppoint.Localization;
using YaAppoint.MultiTenancy;
using YaAppoint.Permissions;
using YaAppoint.Web.HealthChecks;
using YaAppoint.Web.Menus;


namespace YaAppoint.Web;

[DependsOn(
    typeof(YaAppointHttpApiModule),
    typeof(YaAppointApplicationModule),
    typeof(YaAppointEntityFrameworkCoreModule),
    typeof(AbpAutofacModule),
    typeof(AbpIdentityWebModule),
    typeof(AbpAspNetCoreMvcModule),
    typeof(AbpStudioClientAspNetCoreModule),
    typeof(AbpAccountWebOpenIddictModule),
    typeof(AbpFeatureManagementWebModule),
    typeof(AbpSwashbuckleModule),
    typeof(AbpAspNetCoreSerilogModule),
    typeof(AbpLocalizationModule),
    typeof(AbpAspNetCoreMvcUiLeptonXLiteThemeModule)
)]
public class YaAppointWebModule : AbpModule
{
    public override void PreConfigureServices(ServiceConfigurationContext context)
    {
        var hostingEnvironment = context.Services.GetHostingEnvironment();
        var configuration = context.Services.GetConfiguration();

        // 预配置
        context.Services.PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options =>
        {
            options.AddAssemblyResource(
                typeof(YaAppointResource),
                typeof(YaAppointDomainModule).Assembly,
                typeof(YaAppointDomainSharedModule).Assembly,
                typeof(YaAppointApplicationModule).Assembly,
                typeof(YaAppointApplicationContractsModule).Assembly,
                typeof(YaAppointWebModule).Assembly
            );
        });

        PreConfigure<OpenIddictBuilder>(builder =>
        {
            builder.AddValidation(options =>
            {
                options.AddAudiences("YaAppoint");
                options.UseLocalServer();
                options.UseAspNetCore();
            });
        });

        if (!hostingEnvironment.IsDevelopment())
        {
            PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
            {
                options.AddDevelopmentEncryptionAndSigningCertificate = false;
            });

            PreConfigure<OpenIddictServerBuilder>(serverBuilder =>
            {
                serverBuilder.AddProductionEncryptionAndSigningCertificate("openiddict.pfx", "123456");
                //serverBuilder.SetIssuer(new Uri(configuration["AuthServer:Authority"]!));
            });
        }
    }

    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        var hostingEnvironment = context.Services.GetHostingEnvironment();
        var configuration = context.Services.GetConfiguration();

        if (!hostingEnvironment.IsDevelopment())
        {
            // 显式配置ABP防伪选项
            Configure<AbpAntiForgeryOptions>(options =>
            {
                options.AutoValidateIgnoredHttpMethods = new HashSet<string>
                {
                    "GET",
                    "HEAD",
                    "POST",
                    "DELETE"
                };
                options.AutoValidate = true;
                options.AutoValidateFilter = type => true;
                options.TokenCookie.Name = ".AspNetCore.Antiforgery";
                options.TokenCookie.SecurePolicy = CookieSecurePolicy.None; // 允许HTTP
            });

            // 配置ASP.NET Core防伪
            context.Services.AddAntiforgery(options =>
            {
                options.HeaderName = "RequestVerificationToken";
                options.Cookie.Name = ".AspNetCore.Antiforgery";
                options.Cookie.SecurePolicy = CookieSecurePolicy.None; // 允许HTTP
                options.Cookie.SameSite = SameSiteMode.Lax;
            });

        }

        
        Configure<AbpClockOptions>(options =>
        {
            options.Kind = DateTimeKind.Local;

        });


        if (!configuration.GetValue<bool>("App:DisablePII"))
        {
            Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
            Microsoft.IdentityModel.Logging.IdentityModelEventSource.LogCompleteSecurityArtifact = true;
        }

        if (!configuration.GetValue<bool>("AuthServer:RequireHttpsMetadata"))
        {
            Configure<OpenIddictServerAspNetCoreOptions>(options =>
            {
                options.DisableTransportSecurityRequirement = true;
            });

            Configure<ForwardedHeadersOptions>(options =>
            {
                options.ForwardedHeaders = ForwardedHeaders.XForwardedProto;
            });
        }



        ConfigureBundles();
        ConfigureUrls(configuration);
        ConfigureHealthChecks(context);
        ConfigureAuthentication(context);
        ConfigureAutoMapper();
        ConfigureVirtualFileSystem(hostingEnvironment);
        ConfigureNavigationServices();
        ConfigureAutoApiControllers();
        ConfigureSwaggerServices(context.Services);
        ConfigureAuditingLog(context);
        ConfigureIdentity(context);
        ConfigureExctionLocalization(context);
        ConfigureTheme(context);



        Configure<PermissionManagementOptions>(options =>
        {
            options.IsDynamicPermissionStoreEnabled = false;
        });

        if (context.Services.GetSingletonInstance<IWebHostEnvironment>().IsDevelopment())
        {
            Configure<AbpAntiForgeryOptions>(options =>
            {
                options.AutoValidate = false; // 禁用自动验证
            });
        }

        Configure<RazorPagesOptions>(options =>
        {
            // 页面权限
            options.Conventions.AuthorizePage("/Departments/Index", YaAppointPermissions.Departments.Default);
            options.Conventions.AuthorizePage("/DepartmentUsers/Index", YaAppointPermissions.DepartmentUsers.Default);
            options.Conventions.AuthorizePage("/DeviceTypes/Index", YaAppointPermissions.DeviceTypes.Default);
            options.Conventions.AuthorizePage("/Rooms/Index", YaAppointPermissions.Rooms.Default);
            options.Conventions.AuthorizePage("/Devices/Index", YaAppointPermissions.Devices.Default);
            options.Conventions.AuthorizePage("/ExamItems/Index", YaAppointPermissions.ExamItems.Default);
        });

        // 动态声明工厂
        Configure<AbpClaimsPrincipalFactoryOptions>(options =>
        {
            options.IsDynamicClaimsEnabled = false; // 禁用后权限管理对话框不会生成额外权限
        });
    }

    private void ConfigureIdentity(ServiceConfigurationContext context)
    {
        Configure<IdentityOptions>(options =>
        {
            options.User.RequireUniqueEmail = false;
            options.Lockout.AllowedForNewUsers = false;
            options.Lockout.MaxFailedAccessAttempts = 10; // 最多10次错误后锁定

            options.Password.RequireDigit = false;
            options.Password.RequiredLength = 6;
            options.Password.RequiredUniqueChars = 0;
            options.Password.RequireLowercase = false;
            options.Password.RequireUppercase = false;
            options.Password.RequireNonAlphanumeric = false;

            options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
            options.SignIn.RequireConfirmedEmail = false;
            options.SignIn.RequireConfirmedPhoneNumber = false;
            options.SignIn.RequireConfirmedAccount = false;
        });
    }

    /// <summary>
    /// 配置健康检查
    /// </summary>
    /// <param name="context"></param>
    private void ConfigureHealthChecks(ServiceConfigurationContext context)
    {
        context.Services.AddYaAppointHealthChecks();
    }

    private void ConfigureExctionLocalization(ServiceConfigurationContext context)
    {
        Configure<AbpExceptionLocalizationOptions>(options =>
        {
            options.MapCodeNamespace("Ya.Error", typeof(YaAppointResource));
        });
    }

    /// <summary>
    /// 打包压缩
    /// </summary>
    private void ConfigureBundles()
    {
        Configure<AbpBundlingOptions>(options =>
        {
            options.Mode = BundlingMode.None;

            // 全局CSS包
            options.StyleBundles.Configure(
                LeptonXLiteThemeBundles.Styles.Global,
                bundle =>
                {
                    bundle.AddFiles("/global-styles.css");
                    bundle.AddFiles("/element-ui/lib/theme-chalk/index.css");
                }
            );

            // 全局脚本包
            options.ScriptBundles.Configure(
                LeptonXLiteThemeBundles.Scripts.Global,
                bundle =>
                {
                    bundle.AddFiles("/global-scripts.js");
                    bundle.AddFiles("/js/vue.js");
                    bundle.AddFiles("/element-ui/lib/index.js");
                    bundle.AddFiles("/js/Common.js");
                }
            );
        });
    }

    private void ConfigureUrls(IConfiguration configuration)
    {
        Configure<AppUrlOptions>(options =>
        {
            options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];
        });
    }

    /// <summary>
    /// 身份验证
    /// </summary>
    /// <param name="context"></param>
    private void ConfigureAuthentication(ServiceConfigurationContext context)
    {
        context.Services.ForwardIdentityAuthenticationForBearer(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
        context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
        {
            options.IsDynamicClaimsEnabled = true;
        });
    }

    /// <summary>
    /// AutoMapper
    /// </summary>
    private void ConfigureAutoMapper()
    {
        // AutoMapper配置
        Configure<AbpAutoMapperOptions>(options =>
        {
            options.AddMaps<YaAppointWebModule>();
        });
    }

    /// <summary>
    /// 虚拟文件系统
    /// </summary>
    /// <param name="hostingEnvironment"></param>
    private void ConfigureVirtualFileSystem(IWebHostEnvironment hostingEnvironment)
    {
        Configure<AbpVirtualFileSystemOptions>(options =>
        {
            options.FileSets.AddEmbedded<YaAppointWebModule>();

            // 开发环境先使用物理文件替换嵌入式文件
            if (hostingEnvironment.IsDevelopment())
            {
                options.FileSets.ReplaceEmbeddedByPhysical<YaAppointDomainSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}YaAppoint.Domain.Shared", Path.DirectorySeparatorChar)));
                options.FileSets.ReplaceEmbeddedByPhysical<AbpIdentityWebModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}modules{0}Volo.Abp.Identity{0}src{0}Volo.Abp.Identity.Web", Path.DirectorySeparatorChar)));
                options.FileSets.ReplaceEmbeddedByPhysical<YaAppointDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}YaAppoint.Domain", Path.DirectorySeparatorChar)));
                options.FileSets.ReplaceEmbeddedByPhysical<YaAppointApplicationContractsModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}YaAppoint.Application.Contracts", Path.DirectorySeparatorChar)));
                options.FileSets.ReplaceEmbeddedByPhysical<YaAppointApplicationModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}YaAppoint.Application", Path.DirectorySeparatorChar)));
                options.FileSets.ReplaceEmbeddedByPhysical<YaAppointHttpApiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}YaAppoint.HttpApi", Path.DirectorySeparatorChar)));
                options.FileSets.ReplaceEmbeddedByPhysical<YaAppointWebModule>(hostingEnvironment.ContentRootPath);
            }
        });
    }

    /// <summary>
    /// 导航配置
    /// </summary>
    private void ConfigureNavigationServices()
    {
        Configure<AbpNavigationOptions>(options =>
        {
            options.MenuContributors.Add(new YaAppointMenuContributor());
        });

        Configure<AbpToolbarOptions>(options =>
        {
            options.Contributors.Add(new YaAppointToolbarContributor());
        });
    }

    /// <summary>
    /// 自动api控制器配置
    /// </summary>
    private void ConfigureAutoApiControllers()
    {
        Configure<AbpAspNetCoreMvcOptions>(options =>
        {
            options.ConventionalControllers.Create(typeof(YaAppointApplicationModule).Assembly);
        });
    }

    /// <summary>
    /// Swagger配置
    /// </summary>
    /// <param name="services"></param>
    private void ConfigureSwaggerServices(IServiceCollection services)
    {
        services.AddAbpSwaggerGen(
            options =>
            {
                options.SwaggerDoc("v1", new OpenApiInfo { Title = "YaAppoint API", Version = "v1" });
                options.DocInclusionPredicate((docName, description) => true);
                options.CustomSchemaIds(type => type.FullName);
            }
        );
    }

    /// <summary>
    /// 审计日志 AuditingLog
    /// </summary>
    /// <param name="context"></param>
    private void ConfigureAuditingLog(ServiceConfigurationContext context)
    {
        Configure<AbpAuditingOptions>(options =>
        {
            options.EntityHistorySelectors.AddAllEntities();

            options.EntityHistorySelectors.Add(
                new NamedTypeSelector(
                    "MySelectorName",
                    type =>
                    {
                        if (typeof(IEntity).IsAssignableFrom(type))
                        {
                            return true;
                        }
                        else
                        {
                            return false;
                        }
                    }
                )
            );
        });
    }

    /// <summary>
    /// 主题配置
    /// </summary>
    /// <param name="context"></param> 
    private void ConfigureTheme(ServiceConfigurationContext context)
    {
        Configure<AbpThemingOptions>(options =>
        {
            options.DefaultThemeName = "LeptonXLite";
        });
    }

    /// <summary>
    /// abp应用程序初始化
    /// </summary>
    /// <param name="context"></param>
    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var app = context.GetApplicationBuilder();
        var env = context.GetEnvironment();

        app.UseForwardedHeaders();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseAbpRequestLocalization();

        if (!env.IsDevelopment())
        {
            app.UseErrorPage();
            app.UseHsts();
        }


        app.UseCorrelationId();
        app.MapAbpStaticAssets();
        app.UseAbpStudioLink();
        app.UseRouting();
        //app.UseAbpSecurityHeaders();
        app.UseAuthentication();
        app.UseAbpOpenIddictValidation();

        if (MultiTenancyConsts.IsEnabled)
        {
            app.UseMultiTenancy();
        }

        app.UseUnitOfWork();
        app.UseDynamicClaims(); // ABP动态声明中间件
        app.UseAuthorization();
        app.UseSwagger();  // Swagger中间件
        app.UseAbpSwaggerUI(options =>
        {
            options.SwaggerEndpoint("/swagger/v1/swagger.json", "YaAppoint API");
        });
        app.UseAuditing();  // 审计日志中间件
        app.UseAbpSerilogEnrichers();
        app.UseConfiguredEndpoints();
    }
}
